---------America Coast to Coast--------
A 4am crack                  2017-05-07
-------------------. updated 2017-05-10
                   |___________________

Name: America Coast to Coast
Genre: educational
Year: 1984
Publisher: Mindscape
Platform: Apple //e or later
Media: single-sided 5.25-inch floppy
OS: Pronto-DOS
Previous cracks: none (of this version)
Similar cracks:
  #1142 Zoo Collector
  #1141 Zoo Builder
  #1140 Zoo Goer
  #812 A Brand New View (very different
       loading environment, but the
       underlying protection check is
       the same)
  #811 Rosie the Counting Rabbit
  #202 Squeegee Learns About Drugs

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  immediate disk read error

Locksmith Fast Disk Backup
  unable to read any track

EDD 4 bit copy (no sync, no count)
  no errors, but the copy swings to a
  high track and hangs with the drive
  motor on

Copy ][+ nibble editor
  nothing suspicious

Disk Fixer
  track 0 looks like Pronto-DOS loader,
  but it jumps to $B3C3 after loading
  the RWTS (instead of $9D84)

Why didn't any of my copies work?
  I don't know. Not a lot to go on yet.
  Presumably if there was some
  structural protection I would have
  noticed read errors. Hanging with the
  drive motor on could mean anything.

Next steps:

  1. Search for common markers of a
     runtime protection check
  2. If that fails, trace the boot
  3. If that fails, I dunno, go feed
     the ducks or something?

                   ~

               Chapter 1
 In Which The Ducks Will Have To Wait


Turning to my trusty Disk Fixer sector
editor, I search for "BD 89 C0", a
common instruction to turn on the disk
drive motor. It finds several matches,
but nothing that looks like the start
of a protection check. So we get to
start at the beginning.

T00,S00 is a standard DOS 3.3-style
bootloader. It loads most of the rest
of track 0 into $B600+ and jumps to
$B700, stored on T00,S01, to load the
rest of the disk.

                 --v--
T00,S01
----------- DISASSEMBLY MODE ----------
0000:8E E9 B7       STX   $B7E9
0003:8E F7 B7       STX   $B7F7
0006:A9 01          LDA   #$01
0008:8D F8 B7       STA   $B7F8
000B:8D EA B7       STA   $B7EA
000E:AD E0 B7       LDA   $B7E0
0011:8D E1 B7       STA   $B7E1

; start reading DOS on T02,S00 -- looks
; like ProntoDOS
0014:A9 02          LDA   #$02
0016:8D EC B7       STA   $B7EC
0019:A9 00          LDA   #$00
001B:8D ED B7       STA   $B7ED

; $B7E7 is $B4, so the first sector is
; read into $B300
001E:AC E7 B7       LDY   $B7E7
0021:88             DEY
0022:8C F1 B7       STY   $B7F1

; RWTS read command
0025:A9 01          LDA   #$01
0027:8D F4 B7       STA   $B7F4

; set up globals (normal)
002A:8A             TXA
002B:4A             LSR
002C:4A             LSR
002D:4A             LSR
002E:4A             LSR
002F:AA             TAX
0030:A9 00          LDA   #$00
0032:9D F8 04       STA   $04F8,X
0035:9D 78 04       STA   $0478,X

; read DOS into memory (normal)
0038:20 93 B7       JSR   $B793

; reset stack (normal)
003B:A2 FF          LDX   #$FF
003D:9A             TXS
003E:8E EB B7       STX   $B7EB

; machine initialization (not shown,
; but this is definitely ProntoDOS --
; other DOS variants put this code
; somewhere else)
0041:20 69 BA       JSR   $BA69
0044:20 89 FE       JSR   $FE89

; and continue in a strange spot
0047:4C C3 B3       JMP   $B3C3

                 --^--

Standard unprotected ProntoDOS would
jump to $9D84 at this point, but this
disk has other ideas.

$B300 was on T02,S00, so let's jump
over there and continue disassembly.

                 --v--
T02,S00
----------- DISASSEMBLY MODE ----------
; set up another RWTS read: track $11
00C3:A9 11          LDA   #$11
00C5:8D EC B7       STA   $B7EC

; sector 1
00C8:A9 01          LDA   #$01
00CA:8D ED B7       STA   $B7ED

; into $B400
00CD:A9 B4          LDA   #$B4
00CF:8D F1 B7       STA   $B7F1
00D2:A9 00          LDA   #$00
00D4:8D F3 B7       STA   $B7F3
00D7:8D EB B7       STA   $B7EB

; and go
00DA:A9 B7          LDA   #$B7
00DC:A0 E8          LDY   #$E8
00DE:20 B5 B7       JSR   $B7B5

; and decrypt it (!)
00E1:A0 00          LDY   #$00
00E3:B9 12 B4       LDA   $B412,Y
00E6:49 4C          EOR   #$4C
00E8:99 12 B4       STA   $B412,Y
00EB:C8             INY
00EC:C0 FF          CPY   #$FF
00EE:D0 F3          BNE   $00E3
00F0:EA             NOP
00F1:EA             NOP
00F2:EA             NOP
00F3:EA             NOP
00F4:EA             NOP

; and continue in decrypted code
00F5:4C 12 B4       JMP   $B412

                 --^--

Here's T11,S01:

                 --v--

-------------- DISK EDIT --------------
TRACK $11/SECTOR $01/VOLUME $FE/BYTE$12
---------------------------------------
$00: A0 00 B9 12 20 49 4C 99    @9R IL.
$08: 12 20 C8 C0 FF D0 F3 60   R H@.Ps
$10: EA EA>E5<6F C1 A0 FB E5   jje/A {e
$18: 4C C9 48 C1 B8 FB C1 A7   LIHA8{A'
$20: FB 6C A7 F8 6C BE F8 E5   {,'x,>xe
$28: 4C C9 04 F1 C5 8C F1 C2   LIDqE.qB
$30: 8C 6C 08 F5 E9 61 89 48   .,Hui!.H
$38: 9C BB F1 C2 8C F1 C0 8C   .;qB.q@.
$40: 5C B7 85 B3 9C B8 EC 4C   \7.3.8lL
$48: 84 F1 C2 8C F1 C0 8C 5C   .qB.q@.\
$50: B7 85 99 9C BF 8C 4B 9C   7...?.K.
$58: F5 AA 48 E5 5C 89 48 9C   u*He\.H.
$60: 9C E5 4C C9 48 6C 08 F5   .eLIH,Hu
$68: E9 61 89 48 9C BB F1 C0   i!.H.;q@
$70: 8C 5C B7 85 E6 9C BB F1   .\7.f.;q
$78: C0 8C 5C B7 85 A7 9C A2   @.\7.'."
---------------------------------------
BUFFER 0/SLOT 6/DRIVE 1/MASK OFF/NORMAL

---------------------------------------
COMMAND :

                 --^--

Not much to see, since everything after
byte $12 is encrypted (in the 80s kind
of way where it's just XOR'd with a
constant byte, but still). Amusingly,
there is unencrypted code at byte $00
which decrypts the rest of the sector
in the same way as the bootloader. It
assumes the code is stored at $2000
instead of $B400. Maybe debugging code
left by the original developer?

At this point, you might think I would
need to switch to boot tracing the old
fashioned way so I could see this
decrypted code. But Disk Fixer is very
powerful! It has a "MASK" mode that
allows me to XOR every byte of a sector
with a constant byte -- exactly as the
bootloader is doing after it reads this
sector into memory.

Pressing "X" in Disk Fixer lets me set
the mask:

                 --v--

MASK WITH ADD $00/EOR $4C/AND $FF/OR$00
                      ^^^
                       +--- change this

                 --^--

Then pressing <Ctrl-T> applies the mask
to the current sector:

                 --v--

-------------- DISK EDIT --------------
TRACK $11/SECTOR $01/VOLUME $FE/BYTE$12
---------------------------------------
$00: EC 4C F5 5E 6C 05 00 D5   lLu^,E@U
$08: 5E 6C 84 8C B3 9C BF 2C   ^,..3.?,
$10: A6 A6>A9<23 8D EC B7 A9   &&)#.l7)
$18: 00 85 04 8D F4 B7 8D EB   @.D.t7.k
$20: B7 20 EB B4 20 F2 B4 A9   7 k4 r4)
$28: 00 85 48 BD 89 C0 BD 8E   @.H=.@=.
$30: C0 20 44 B9 A5 2D C5 04   @ D9%-ED
$38: D0 F7 BD 8E C0 BD 8C C0   Pw=.@=.@
$40: 10 FB C9 FF D0 F4 A0 00   P{I.Pt @
$48: C8 BD 8E C0 BD 8C C0 10   H=.@=.@P
$50: FB C9 D5 D0 F3 C0 07 D0   {IUPs@GP
$58: B9 E6 04 A9 10 C5 04 D0   9fD)PEDP
$60: D0 A9 00 85 04 20 44 B9   P)@.D D9
$68: A5 2D C5 04 D0 F7 BD 8C   %-EDPw=.
$70: C0 10 FB C9 AA D0 F7 BD   @P{I*Pw=
$78: 8C C0 10 FB C9 EB D0 EE   .@P{IkPn
---------------------------------------
BUFFER 0/SLOT 6/DRIVE 1/MASK ON /NORMAL

---------------------------------------
COMMAND :

                 --^--

And now we can list the encrypted
routine at byte $12.

                   ~

               Chapter 2
        The Belly Of The Beast


The protection check starts at byte $12
(loaded into memory at $B412):

                 --v--

T09,S01
----------- DISASSEMBLY MODE ----------
; Hmm, seeking to track $23 perhaps?
; That would explain why even my EDD
; bit copy failed -- I didn't copy
; track $23!
0012:A9 23          LDA   #$23
0014:8D EC B7       STA   $B7EC

; 0 = RWTS seek command
0017:A9 00          LDA   #$00
0019:85 04          STA   $04
001B:8D F4 B7       STA   $B7F4

; 0 also = disk volume wildcard
001E:8D EB B7       STA   $B7EB

; get address of RWTS parameter table
; and call RWTS entry point to seek
; (not shown)
0021:20 EB B4       JSR   $B4EB
0024:20 F2 B4       JSR   $B4F2
0027:A9 00          LDA   #$00
0029:85 48          STA   $48

; Oh look! Here's that "BD 89 C0"
; instruction I was looking for earlier
; but couldn't find (because it was
; encrypted)
002B:BD 89 C0       LDA   $C089,X
002E:BD 8E C0       LDA   $C08E,X

; look for the next available address
; field
0031:20 44 B9       JSR   $B944

; compare the sector we found against
; the counter we initialized in zp$04
; earlier
0034:A5 2D          LDA   $2D
0036:C5 04          CMP   $04

; loop until we find the address field
; for track $23, sector $00
0038:D0 F7          BNE   $0031

; skip to sync byte (#$FF)
003A:BD 8E C0       LDA   $C08E,X
003D:BD 8C C0       LDA   $C08C,X
0040:10 FB          BPL   $003D
0042:C9 FF          CMP   #$FF
0044:D0 F4          BNE   $003A

; skip an exact number of nibbles and
; look for #$D5
0046:A0 00          LDY   #$00
0048:C8             INY
0049:BD 8E C0       LDA   $C08E,X
004C:BD 8C C0       LDA   $C08C,X
004F:10 FB          BPL   $004C
0051:C9 D5          CMP   #$D5
0053:D0 F3          BNE   $0048

; if not found in correct location, try
; again from the top (Y register is the
; nibble counter)
0055:C0 07          CPY   #$07
0057:D0 B9          BNE   $0012

; do this for all sectors
0059:E6 04          INC   $04
005B:A9 10          LDA   #$10
005D:C5 04          CMP   $04
005F:D0 D0          BNE   $0031

; start over on sector 0
0061:A9 00          LDA   #$00
0063:85 04          STA   $04
0065:20 44 B9       JSR   $B944
0068:A5 2D          LDA   $2D
006A:C5 04          CMP   $04
006C:D0 F7          BNE   $0065

; skip to address epilogue
006E:BD 8C C0       LDA   $C08C,X
0071:10 FB          BPL   $006E
0073:C9 AA          CMP   #$AA
0075:D0 F7          BNE   $006E
0077:BD 8C C0       LDA   $C08C,X
007A:10 FB          BPL   $0077
007C:C9 EB          CMP   #$EB
007E:D0 EE          BNE   $006E

; skip to sync byte (#$FF)
0080:BD 8C C0       LDA   $C08C,X
0083:10 FB          BPL   $0080
0085:C9 FF          CMP   #$FF
0087:D0 F7          BNE   $0080

; skip an exact number of nibbles and
; look for #$D5
0089:A0 00          LDY   #$00
008B:C8             INY
008C:BD 8E C0       LDA   $C08E,X
008F:BD 8C C0       LDA   $C08C,X
0092:10 FB          BPL   $008F
0094:C9 D5          CMP   #$D5
0096:D0 F3          BNE   $008B

; if not found in correct location, try
; again from the top (Y register is the
; nibble counter again)
0098:C0 10          CPY   #$10
009A:D0 C5          BNE   $0061

; do this for several other sectors
009C:E6 04          INC   $04
009E:A9 0A          LDA   #$0A
00A0:20 A8 FC       JSR   $FCA8
00A3:A9 04          LDA   #$04
00A5:C5 04          CMP   $04
00A7:D0 04          BNE   $00AD
00A9:E6 04          INC   $04
00AB:D0 B8          BNE   $0065
00AD:A9 0F          LDA   #$0F
00AF:C5 04          CMP   $04
00B1:D0 BB          BNE   $006E
00B3:20 44 B9       JSR   $B944
00B6:A5 2D          LDA   $2D
00B8:C9 0F          CMP   #$0F
00BA:D0 F7          BNE   $00B3

; read data field (not shown)
00BC:20 DC B8       JSR   $B8DC

; look for #$A5 nibble in a specific
; place
00BF:A0 00          LDY   #$00
00C1:C8             INY
00C2:BD 8E C0       LDA   $C08E,X
00C5:BD 8C C0       LDA   $C08C,X
00C8:10 FB          BPL   $00C5
00CA:C9 A5          CMP   #$A5
00CC:D0 F3          BNE   $00C1

; if not found in correct location, try
; again from the top (Y register is the
; nibble counter again)
00CE:C0 2B          CPY   #$2B
00D0:D0 E1          BNE   $00B3

; look for #$D5 nibble in a specific
; place
00D2:A0 00          LDY   #$00
00D4:C8             INY
00D5:BD 8E C0       LDA   $C08E,X
00D8:BD 8C C0       LDA   $C08C,X
00DB:10 FB          BPL   $00D8
00DD:C9 D5          CMP   #$D5
00DF:D0 F3          BNE   $00D4

; if not found in correct location, try
; again from the top (Y register is the
; nibble counter again)
00E1:C0 5D          CPY   #$5D
00E3:D0 CE          BNE   $00B3

; success path falls through to here --
; turn off the drive motor and jump
; forward
00E5:BD 88 C0       LDA   $C088,X
00E8:4C F5 B4       JMP   $B4F5

; set RUN flag to disable user control
; in case they manage to get to a BASIC
; prompt
00F5:A9 FF          LDA   #$FF
00F7:85 D6          STA   $D6

; continue with standard boot sequence
00F9:4C 84 9D       JMP   $9D84

                 --^--

My copy never makes it this far. It's
stuck in an infinite loop, trying to
find a precisely organized track $23
(and failing).

It looks like I should be able to skip
over the protection check by jumping
straight to $9D84 instead of $B412.

This is the 4th disk I've found (from
the 2nd publisher) that shares this
protection, so I've added support for
it to the next version of Passport.
Here is the transcript:

                 --v--

READING FROM S6,D1
T00,S00 FOUND PRONTO-DOS BOOTLOADER
USING DISK'S OWN RWTS
WRITING TO S5,D2
T02,S00 DISK CALLS A PROTECTION CHECK
AT $B412 BEFORE INITIALIZING DOS.
T02,S00,$F6: 12B4 -> 849D
CRACK COMPLETE.

                 --^--

More information and source code is
available at
https://archive.org/details/Passport4am

Quod erat liberandum.

                   ~

               Changelog


2017-05-10

- fixed descriptions of automated tools

2017-05-07

- initial release

---------------------------------------
A 4am crack                    No. 1194
------------------EOF------------------
